package app.structure;

import java.io.Serializable;
import java.util.Iterator;
import java.util.HashMap;
import java.util.TreeSet;

import org.commons.Utils;

public class Graph implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -851011680046251268L;
	
	private HashMap<Integer, String> hashPathMap;
	private HashMap<Integer, TreeSet<Integer>> hashGraph;
	private String prefix;
	private String startPath;
	private int startHash;


	public Graph(String prefix, String suffix) {
		this.hashPathMap = new HashMap<Integer, String>();
		this.hashGraph = new HashMap<Integer, TreeSet<Integer>>();
		this.prefix = prefix;
		this.startPath = suffix;

		Integer suffixHash = Utils.hash(this.startPath);
		this.startHash = suffixHash;
		hashPathMap.put(suffixHash, suffix);
		hashGraph.put(suffixHash, new TreeSet<Integer>());
	}

	public void insertAdjacentNodes(String node1, String node2) {
		Integer node1Hash = Utils.hash(node1);
		hashPathMap.put(node1Hash, node1);
		TreeSet <Integer> node1List = hashGraph.get(node1Hash);
		if (node1List == null) {
			node1List = new TreeSet<Integer>();
			hashGraph.put(node1Hash, node1List);
		}

		if(node2 != null) {
			Integer node2Hash = Utils.hash(node2);
			hashPathMap.put(node2Hash, node2);
			node1List.add(node2Hash);

			TreeSet <Integer> node2List = hashGraph.get(node2Hash);
			if (node2List == null) {
				node2List = new TreeSet<Integer>();
				hashGraph.put(node2Hash, node2List);
			}
			node2List.add(node1Hash);
		}
	}

	public boolean existsNode(String url) {
		return hashPathMap.containsValue(url);
	}

	public boolean existsNode(Integer hash) {
		return hashPathMap.containsKey(hash);
	}

	/**
	 * il metodo next prende come parametro un intero (che rappresenta la pagina corrente) e restituisce
	 * una stringa che rappresenta un url all'interno della pagina corrente (in pratica viene scelto un)
	 * elemento a caso all'interno dell'array corrispondente a currentPage
	 * 
	 * questo metodo viene utilizzato per gli attacchi di tipo 3
	 */
	public int next(int currentPage) {
		TreeSet<Integer> temp = hashGraph.get(currentPage);
		if(temp != null && temp.size() != 0) {
			Iterator<Integer> iti = temp.iterator();
			int rand = (int) (Math.random() * temp.size());
			int returnValue = 0;
			for(int i = 0; i < rand; i++) {
				returnValue = iti.next();
			}
			return returnValue;
		}
		else
			return 0;
	}

	public String getPrefix() {
		return prefix;
	}

	public String getStartPath() {
		return startPath;
	}

	public int getStartHash() {
		return startHash;
	}

	public HashMap<Integer, String> getHashPathMap() {
		return hashPathMap;
	}

	public void setHashPathMap(HashMap<Integer, String> hashPathMap) {
		this.hashPathMap = hashPathMap;
	}

	/**
	 * 
	 * @param hash
	 * @return il path assoluto ottenuto concatenando il {@link #prefix}
	 *  con il path relativo rappresentato da @param hash
	 */
	public String getAbsolutePath(Integer hash) {
		return prefix + getRelativePath(hash);
	}

	/**
	 * 
	 * @param hash
	 * @return il path assoluto ottenuto concatenando il {@link #prefix}
	 *  con il path relativo rappresentato da @param hash
	 */
	public String getRelativePath(Integer hash) {
		return hashPathMap.get(hash);
	}

	public HashMap<Integer, TreeSet<Integer>> getHashGraph() {
		return hashGraph;
	}

	public void setHashGraph(HashMap<Integer, TreeSet<Integer>> hashGraph) {
		this.hashGraph = hashGraph;
	}
	
	public TreeSet<Integer> getAdjacencyList(Integer hash) {
		return hashGraph.get(hash);
	}
	
	public String toString() {
		return "startPath: " + prefix + startPath + "\nhashPathMap: " + hashPathMap.toString() + "\nhashGraph: " + hashGraph.toString();
	}

}